#simple in-memory database engine
#TODO:
#
#add __getstate__ and __setstate__ functionality to all classes in order to reduce memory usage (I'll do it a bit later)
#
#add Indexes functionality (BTREE, HASH)
#and all other good things
#
#???
#
#PROFIT!



import bz2
import sys
import csv
import pickle

path='mydbfile'
global myDB

            
class myint(object):
    #__slots__='value'
    #def __getstate__(self)
    #def __setstate__(self)
    def __init__(self, value='NULL'):
        if isinstance(int(value), int):
            self.value=value
        else:
            print "Error. Can't convert value to type INT"
    def __repr__(self):
        return str(self.value)

class double(object):
    #__slots__='value'
    def __init__(self, value='NULL'):
        if isinstance(float(value), float) or isinstance(int(value), int):
            self.value=float(value)
        else:
            print "Error. Can't convert value to type DOUBLE"
    def __repr__(self):
        return str(self.value)
        
class varchar(object):
    #__slots__=('size', 'data')
    def __init__(self, data=""):
            self.size=len(data)
            self.data=data
    def __repr__(self):
        return str(self.data)
            

types_dict={'INT':myint, 'VARCHAR':varchar, 'DOUBLE':double}




class entry(object):
    #__slots__='data'
    #headers_dict=dict(headers)
    
    
    def __init__(self, headers, columns, values):
        self.data=[]
        if len(columns)!=len(values):
            print "All went wrong because of YOU"
        for i in headers:
            this_header_found_in_parameters=False
            for j in columns:
                
                if i[0]==j:
                    newvalue=types_dict[i[1]](values[columns.index(j)])
                    self.data.append(newvalue)
                    this_header_found_in_parameters=True
            
            if this_header_found_in_parameters==False:
                newvalue=types_dict[i[1]]()
                self.data.append(newvalue)
        
    def __repr__(self):
        return str(self.data)            
            
                


class table(object):
    #__slots__=('name','headers','contents')
    def __init__(self,name,parlist):
        self.name=name 
        self.headers=parlist
        self.contents=[]

    def update(self, data):
        pass
    def __repr__(self):
        return str(self.name)+str(self.headers)
    def add_entry(self, _entry):
        self.contents.append(_entry)
            

def load_db(path):
        try:
            db_file=open(path, 'r')
            mydb=pickle.load(db_file)
            db_file.close()
        except IOError:
            print 'Error reading from file' % path
        assert isinstance(mydb, db), 'Could not load database from file'
        return mydb
def save_db(path):
        db_file=open(path, 'w+')
        pickle.dump(myDB,db_file)
        db_file.close()
        print "Successfully saved"



class db:
     
    
    def __init__(self):
        self.contents={}    #namely sys.tables
    def create_table(self, tablename, parlist):
        newtable=table(tablename, parlist) ##don't forget 
        self.contents[tablename]=newtable
    def insert_into_table(self, tablename,columns, values):
        if tablename in self.contents:
            new_entry=entry(self.contents[tablename].headers, columns, values)
            self.contents[tablename].add_entry(new_entry)
        else:
            print "INSERT error. No such table"
    def select_all_from_table(self, tablename):
        if tablename in self.contents:
            for i in self.contents[tablename].headers:
                print i[1],
            print
            for i in self.contents[tablename].contents:
                print i,
            
                
        

def parse_sql_query(query):
    operation_successful=False
    if query.find('(')!=-1: 
        tokens=query[:query.find('(')]
    else:
        tokens=query
    tokens=tokens.split()
    if tokens[0]=='CREATE':
        if tokens[1]=='TABLE':
            tablename=tokens[2]
            
            #parsing parameters step by step (removing odd spaces, splitting etc)
            params=query[query.find('(')+1:query.find(')')]
            params=' '.join(params.split())
            params=params.split(', ')
            params=[tuple(par.split()) for par in params]
            myDB.create_table(tablename, params)
            print "OK"            
    if tokens[0]=='INSERT':
        if tokens[1]=='INTO':
            tablename=tokens[2]
            columns=query[query.find('(')+1:query.find(')')]
            columns=columns.split(', ')
            vpos=query.find('VALUES')
            values=query[query.find('(', vpos+1)+1:query.find(')', vpos+1)]
            values=values.split(', ')
            myDB.insert_into_table(tablename, columns, values)
            print "OK"
    if tokens[0]=='SELECT':
        if tokens[1]=='*':
            if tokens[2]=='FROM':
                tablename=tokens[3]
                myDB.select_all_from_table(tablename)
        

myDB=db()
#myDB=load_db(path)

q="CREATE TABLE NOMER1 (pole1 INT, pole2 DOUBLE, pole3 DOUBLE)"
parse_sql_query(q)
print myDB.contents
w="INSERT INTO NOMER1 (pole1, pole2, pole3) VALUES (2, 2.5, 3.141592)"
parse_sql_query(w)
w="INSERT INTO NOMER1 (pole1, pole2, pole3) VALUES (3, 4.5, 2.717281)"
parse_sql_query(w)
#print myDB.contents['NOMER1']
print

r="SELECT * FROM NOMER1"
parse_sql_query(r)
save_db(path)
    
#path='dbfile.db'
#myDB=db()

#sql='CREATE TABLE T1 (n1 t1, n2 t2, n3 t3)'
#t=parse_sql_query(sql)
#sql='CREATE TABLE T2 (n2 t2, n4 t4, n5 t6)'
#t=parse_sql_query(sql)
#myDB.save(path)
#myDB.load('dbfile.db')
#print myDB.contents